﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using KPM.Common;

namespace KPM.Common.DragHelpers
{
    public class CanvasDragListBox : ListBox
    {
        /// <summary>
        /// Dependency property declaration for <see cref="AllowDrag" />.
        /// </summary>
        public static readonly DependencyProperty AllowDragProperty = DependencyProperty.Register("AllowDrag", typeof(bool), typeof(CanvasDragListBox), new PropertyMetadata(false));

        /// <summary>
        /// Dependency property declaration for <see cref="MaxTop" />.
        /// </summary>
        public static readonly DependencyProperty MaxTopProperty = DependencyProperty.Register("MaxTop", typeof(double), typeof(CanvasDragListBox), new PropertyMetadata(0d));

        /// <summary>
        /// Gets or sets a value indicating whether [allow drag].
        /// </summary>
        /// <value><c>true</c> if [allow drag]; otherwise, <c>false</c>.</value>
        public bool AllowDrag
        {
            get
            {
                return (bool)GetValue(AllowDragProperty);
            }

            set
            {
                SetValue(AllowDragProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the max left for drag.
        /// </summary>
        /// <value>The max left.</value>
        public double MaxLeft { get; set; }

        /// <summary>
        /// Gets or sets the max top for drag.
        /// </summary>
        /// <value>The max top.</value>
        public double MaxTop
        {
            get
            {
                return (double)GetValue(MaxTopProperty);
            }

            set
            {
                SetValue(MaxTopProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the canvas top binding.
        /// </summary>
        /// <value>The canvas top binding.</value>
        public BindingDefinition CanvasTopBinding { get; set; }

        /// <summary>
        /// Gets or sets the canvas left binding.
        /// </summary>
        /// <value>The canvas left binding.</value>
        public BindingDefinition CanvasLeftBinding { get; set; }

        /// <summary>
        /// Gets or sets the rotation binding converter.
        /// </summary>
        /// <value>The rotation binding converter.</value>
        public IValueConverter RotationBindingConverter { get; set; }

        /// <summary>
        /// Creates or identifies the element used to display a specified item.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Windows.Controls.ListBoxItem"/> corresponding to a specified item.
        /// </returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            ListBoxItem containerForItemOverride = new CustomListBoxItem();
            Style itemContainerStyle = this.ItemContainerStyle;
            if (itemContainerStyle != null)
            {
                containerForItemOverride.Style = itemContainerStyle;
            }

            return containerForItemOverride;
        }

        /// <summary>
        /// Prepares the specified element to display the specified item.
        /// </summary>
        /// <param name="element">The element used to display the specified item.</param>
        /// <param name="item">The item to display</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            CanvasDragType canvasDragType = CanvasDragType.Unknown;
            BindingDefinition topBinding = this.CanvasTopBinding;
            if (topBinding != null)
            {
                if (topBinding.AllowDrag)
                {
                    canvasDragType = CanvasDragType.TopOnly;
                }

                BindingOperations.SetBinding(
                    element,
                    Canvas.TopProperty,
                    new Binding(topBinding.Path) { Source = item, Mode = BindingMode.TwoWay, Converter = topBinding.Converter });
            }

            BindingDefinition leftBinding = this.CanvasLeftBinding;
            if (leftBinding != null)
            {
                if (leftBinding.AllowDrag)
                {
                    canvasDragType = canvasDragType == CanvasDragType.TopOnly
                                         ? CanvasDragType.LeftAndTop
                                         : CanvasDragType.LeftOnly;
                }

                BindingOperations.SetBinding(
                    element,
                    Canvas.LeftProperty,
                    new Binding(leftBinding.Path) { Source = item, Mode = BindingMode.TwoWay, Converter = leftBinding.Converter });
            }

            IRotate rotate = item as IRotate;
            if (rotate != null)
            {
                UIElement uiElement = (UIElement)element;
                uiElement.RenderTransformOrigin = new Point(.5, 0);
                RotateTransform rotateTransform = new RotateTransform();
                uiElement.RenderTransform = rotateTransform;
                BindingOperations.SetBinding(
                    rotateTransform,
                    RotateTransform.AngleProperty,
                    new Binding("Rotation") { Source = item, Mode = BindingMode.TwoWay, Converter = this.RotationBindingConverter });
            }

            MouseInteraction.SetCanvasDrag(
                (UIElement)element,
                new CanvasDragDefinition(() => this.AllowDrag, this.MaxLeft, this.MaxTop) { DragType = canvasDragType });
            base.PrepareContainerForItemOverride(element, item);
        }

        /// <summary>
        /// Custom <see cref="ListBoxItem"/> to modify mouse behavior
        /// </summary>
        private class CustomListBoxItem : ListBoxItem
        {
            /// <summary>
            /// Provides handling for the <see cref="E:System.Windows.UIElement.MouseLeftButtonDown"/> event.
            /// </summary>
            /// <param name="e">The event data.</param>
            protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
            {
                base.OnMouseLeftButtonDown(e);
                e.Handled = false;
            }
        }
    }
}